<?php
class FeedReaderComponent extends Object {
	var $downloadDisabled = false;
	var $defaultUserAgent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9';
//	var $defaultUserAgent = 'RSSDownloader0.01 http://code.google.com/p/rssdownloader/'; 

	function initialize(&$controller, $settings = array()) {
		// saving the controller reference for later use
		$this->controller =& $controller;
		$this->Feed = ClassRegistry::init('Feed'); 
	}
	//called after Controller::beforeFilter()
	function startup(&$controller) {
	}

	//called after Controller::beforeRender()
	function beforeRender(&$controller) {
	}

	//called after Controller::render()
	function shutdown(&$controller) {
	}

	//called before Controller::redirect()
	function beforeRedirect(&$controller, $url, $status=null, $exit=true) {
	}

	function redirectSomewhere($value) {
		// utilizing a controller method
		$this->controller->redirect($value);
	}

	function scan($id = null) {
		if(isset($id)) {
			$feeds = array($id);
		} else {
			$feeds = $this->Feed->find('all', array('fields'=>'id', 'contain'=>false,'recursive'=>'-1'));
			$feeds = Set::extract($feeds,'{n}.Feed.id');
		}
	
		foreach($feeds as $id) {
			$feed = $this->Feed->find('first',array(
				'conditions'=>array('id'=>$id),
				'contain'=>(array('Favorite'=>array('Download')))
			));
			$this->out("Scanning {$feed['Feed']['name']}...");
			$faves = $this->Feed->Favorite->find('all',array(
				'conditions'=>array('feed_count'=>0),
				'contain'=>array('Download'=>array('conditions'=>array('category_id'=>$feed['Feed']['category_id'])))
			));
			foreach($faves as $f=>$v) {
				$faves[$f]['Favorite']['download'] = array_flip(Set::extract($v,'Download.{n}.match'));
			}
			$faves = Set::extract($faves,'{n}.Favorite');
	
			$feed['Favorite'] = array_merge($feed['Favorite'],$faves);
			$this->processMatches($this->findMatches($feed),$feed['Feed']);
//			$this->set('feed', $feed);
		} 
	}


	function loadFeed($furl, $lastmodified = null) {
		ini_set('user_agent', $this->defaultUserAgent);
		
		$ch = curl_init($furl);
		curl_setopt_array($ch, array(
			CURLOPT_FOLLOWLOCATION=>true,
			CURLOPT_AUTOREFERER=>true,
			CURLOPT_RETURNTRANSFER=>true,
			CURLOPT_FRESH_CONNECT=>true,
			CURLOPT_HEADER=>true,
			CURLOPT_NOBODY=>true,
			CURLOPT_TIMEVALUE=> strtotime($lastmodified),
			CURLOPT_TIMECONDITION=>CURL_TIMECOND_IFMODSINCE,
			CURLOPT_USERAGENT=>$this->defaultUserAgent
			));
		$remote_file = curl_exec($ch);
		$res = curl_getinfo($ch);
		curl_close($ch);
		if($res['http_code']!=200) 
			return;
		libxml_use_internal_errors(true);
		$xml = simplexml_load_file($furl);
		$errors = libxml_get_errors();
		if(sizeof($errors) > 0){
			Configure::write('debug',2);
			debug($errors);
//			$this->out("Error loading feed");
			libxml_clear_errors();
			return false;
		}
		return $xml;		
	}

	function findMatches($feed) {
		/*
		 * Finds items matching the active Favorites
		 */
		$xml = $this->loadFeed($feed['Feed']['url'], $feed['Feed']['updated']);
		if(!$xml) {
			return false;
		}
		$faves = $feed['Favorite'];
		$matches = array();
		foreach($xml->channel->item as $item) {
			//TODO: Allow for feed-wide filter (es. VO, 720p, etc)
			if($feed['Feed']['filter'] && !preg_match('/'.$feed['Feed']['filter'].'/',$item->$feed['Feed']['match_field'])) {
				continue;
			}
			foreach($faves as $fave) {
				if(preg_match('/'.$fave['regex'].'/i', $item->$feed['Feed']['match_field'])) {
					$this->out('Match found for '.$fave['title'].' in '.$item->title);
					array_push($matches, array('rss'=>$item,'fave'=>$fave));
				} else {
//					$this->out('No match for '.$item->$feed['Feed']['match_field']);
				}
			}
		}
		return $matches;
	}

	function cleanupPage($html) {
		/* @return properly cleaned up xml
		 * 
		 */
		$tidy_config = array(
			'escape-cdata'=>true,
			'wrap'=>0,
			'add-xml-decl'=>true,
			'output-xml'=>true,
			'char-encoding'=>'ascii',
			'numeric-entities'=>true);
		$html = tidy_repair_string($html, $tidy_config);
		$html = preg_replace(array('/[^\x01-\x7E]/','/xmlns[^"]+"[^"]+"/i','/<\/?tbody[^>]*>/i'),'',$html);
		$xml = simplexml_load_string($html);
		$errors = libxml_get_errors();
		if(sizeof($errors) > 0){
			$this->out($errors);
			libxml_clear_errors();
			exit;
		}
		return $xml;		
	}

	function processMatches($matches, $feed) {
		if(!$matches || count($matches)<1) {
			return;
		}
		libxml_use_internal_errors(true);
		foreach($matches as $item) {
			if(!empty($feed['save_dir'])) { // Feed save_dir overrides the favorite one
				$savedir = preg_replace('/\/$/','',$feed['save_dir']).'/';
			}
			elseif(!empty($item['fave']['save_dir'])) {
				$savedir = preg_replace('/\/$/','',$item['fave']['save_dir']).'/';
			} else {
				$savedir = "C:/stuff/"; //TODO: Not good.
			}
						
			$this->metadata = array();
			$referrer = false;
			if ($feed['download_search'] && $feed['download_replace']) {
				$downloadurl = preg_replace($feed['download_search'], $feed['download_replace'],$item['rss']->link);
				$referrer = $item['rss']->link;
			} else if($feed['download_xpath']) {
				$file = file_get_contents($item['rss']->link);
				$xml = $this->cleanupPage($file);
				list($_) = $xml->xpath(str_replace('/tbody','',$feed['download_xpath'])); //removing tbody from both to be sure
				$_ = $_->attributes();
				$downloadurl = preg_replace('/#HREF#/',$_['href'],$feed['url_pattern']);
			} else if ($feed['download_regex']) {
				$html = file_get_contents($page);
				preg_match("/".str_replace('/','\/',$feed['download_regex'])."/im",$html,$_);
				if(count($_)) {
					$downloadurl = $_[1];
				}
			} else { //assuming direct download - maybe use a flag?
				if($item['rss']->enclosure['url']) {
					$downloadurl = $item['rss']->enclosure['url'];
				} else {
					$downloadurl = $item['rss']->link;
				}
				
			}
			$this->out("Downloading from $downloadurl");
			//Now we have pretty much all the info required to check for dupes
			$normalizedInfo = $this->normalizeMatch(preg_replace('/'.$item['fave']['regex'].'/','',$item['rss']->$feed['match_field']),$downloadurl);
			$this->out($normalizedInfo);


			if(isset($item['fave']['download'][$normalizedInfo])) {
				$this->out('Dupe found, skipping!');
				continue;
			}

			//store the required values for downloads here, will save when the file has been downloaded.
			$downloaddata = array(
				'favorite_id'=>$item['fave']['id'],
				'category_id'=>$feed['category_id'],
				'match'=>$normalizedInfo
			);
			
			
			//TODO: temporarily moved here, should be near the start
			if($this->downloadDisabled) {
				continue;
			}
			
			//let's try with curl
			$ch = curl_init($downloadurl);
			curl_setopt_array($ch, array(
				CURLOPT_HEADERFUNCTION=>array(&$this,'readHeader'),
				CURLOPT_FOLLOWLOCATION=>true,
				CURLOPT_AUTOREFERER=>true,
				CURLOPT_RETURNTRANSFER=>true,
				CURLOPT_FRESH_CONNECT=>true,
				CURLOPT_HEADER=>false,
				CURLOPT_USERAGENT=>$this->defaultUserAgent
				));
			if($referrer) {
				curl_setopt($ch, CURLOPT_REFERER, $referrer);
			}	
				
			$remote_file = curl_exec($ch);
			$this->metadata = array_merge(curl_getinfo($ch), $this->metadata);
			curl_close($ch);
		
			if(!$remote_file) {
				$this->out('Unable to open target file. Reason: '.curl_error($ch));
				return; 
			}
			if(!isset($this->metadata['filename'])) {
				//let's try with the url
				$this->out("Failed to retrieve filename from the headers");
				$this->metadata['filename'] = urldecode(basename($this->metadata['url']));
			}
			$filename = trim($this->metadata['filename']);

			
			if(file_exists($savedir.$filename)) { //Shouldn't actually ever happen
				$this->out("File {$savedir}$filename already exists; skipping.");
			} else {
				if (file_put_contents($savedir.$filename, $remote_file, LOCK_EX)) {
					$this->out("File written: {$savedir}$filename");
					
					//Update download history with the file.
					$downloaddata['filename'] = "{$savedir}$filename";
					$this->Feed->Favorite->Download->create();
					$this->Feed->Favorite->Download->save($downloaddata);
					
				} else {
					$this->out("Unable to write {$savedir}$filename");
				}
			}
		}
	}

	function readHeader($ch, $header) {
		if(preg_match('/Content-Disposition: attachment; filename="?([^"]*)"?/i',$header, $result)) {
			$this->metadata['filename'] = $result[1];
		}
//		$this->out($header);
		return strlen($header);
	}

	function normalizeMatch($title, $file) {
		//Start from the title then proceed to the filename if required
		
		preg_match('/\s*\b[sS]?(\d{1,2})[xXeE]?(\d{2})\b.*/',$title,$match);
		preg_match('/REPACK|PROPER|RERIP/i',$title,$proper);
		
		return sprintf("%02d.%02d.%d",$match[1],$match[2],count($proper));
	}
	
	function out($msg) {
		$this->controller->out($msg); 
	}

}
?>